-- NetPack
local skynet = require "skynet";
local log = require "log";

local ENUM_REQUEST_TYPE = 
{
    REQUEST     = 1,    -- 请求
    RESPONSE    = 2,    -- 回应
}

-- netpack api
local netpack = {};

local ENUM_BOOL_USEHEADEX = true;

-- set use head ex
function netpack.setuseheadex(buse)
    ENUM_BOOL_USEHEADEX = buse;
end

-- create net packet
function netpack.create(pbuffer, wprototype, wprotoid, cbencrypttype, cbcompresstype)
    assert(pbuffer, "usnetpack buffer is null");
    local ret = {};

    -- 扩展头
    ret.headex = {};
    ret.headex.encrypt_type = cbencrypttype or 0;
    ret.headex.compress_type = cbcompresstype or 0;
    -- 包头
    ret.head = {};
    ret.head.request_type = 1;
    ret.head.session_id = 0;
    ret.head.proto_type = wprototype;
    ret.head.proto_id = wprotoid;
    -- 数据
    ret.data = {};
    ret.data.buffer = pbuffer;

    return ret;
end

-- set protocol
function netpack.set_protocol(packet, wprototype, wprotoid)
    assert(wprototype);
    assert(wprotoid);
    assert(type(packet.head) == "table");

    packet.head.proto_type = wprototype;
    packet.head.proto_id = wprotoid;
end

-- set encrypt type
function netpack.set_encrypt_type(packet, cbencrypttype)
    assert(cbencrypttype);
    assert(type(packet.headex) == "table");

    packet.headex.encrypt_type = cbencrypttype;
end

-- set compress type
function netpack.set_compress_type(packet, cbcompresstype)
    assert(cbcompresstype);
    assert(type(packet.headex) == "table");

    packet.headex.compress_type = cbcompresstype;
end

-- encode headex
local function _encode_headex(packet, inbuffer, sz)
    local buffer = "";
    local headex = packet.headex;

    -- encrypt
    if 0 ~= headex.encrypt_type then
        -- mark: wait implement
    end

    -- compress
    if 0 ~= headex.compress_type then
        -- mark: wait implement
    end

    buffer = string.pack(">I2BBc"..tostring(sz), sz+2, headex.encrypt_type, headex.compress_type, inbuffer);
    sz = sz + 2;

    return buffer, sz;
end

-- encode head
local function _encode_head(packet, inbuffer, sz)
    local buffer = "";
    local head = packet.head
    local size = sz + 11;

    buffer = string.pack(">HBI4HHc"..tostring(sz), sz+9, head.request_type, head.session_id, head.proto_type, head.proto_id, inbuffer);

    return buffer, size;
end

-- encode data
local function _encode_data(packet, inbuffer, sz)
    local buffer = "";
    local data = packet.data;
    local size = 0;

    buffer = string.pack(">s2", data.buffer) .. inbuffer;
    size = string.unpack(">H", buffer) + 2;
    sz = sz + size

    return buffer, sz
end

-- encode packet
function netpack.encode_packet(packet)
    local buffer = "";
    local sz = 0;

    -- data
    buffer, sz = _encode_data(packet, buffer, sz);

    -- head
    buffer, sz = _encode_head(packet, buffer, sz);

    -- headex
    if ENUM_BOOL_USEHEADEX then
        buffer = _encode_headex(packet, buffer, sz);
    end
    -- log.dump(packet, "response");
    return buffer, sz;
end

-- encode buffer
function netpack.encode(pbuffer, wprototype, wprotoid, cbencrypttype, cbcompresstype)
    return netpack.encode_packet(netpack.create(pbuffer, wprototype, wprotoid, cbencrypttype, cbcompresstype));
end

-- decode net packet headex
local function _decode_headex(pbuffer, sz)
    -- decode failed
    if 2 > sz then
        log.printf("usnetpack _decode_headex sz error! sz==>%s", sz);
        return;
    end

    local ret = {};

    local noffset = nil;
    local cbencrypttype;
    local cbcompresstype;
    cbencrypttype, cbcompresstype, noffset = string.unpack(">BB", pbuffer, noffset);

    -- decode failed
    local rdsz = noffset - 1;
    if 2 ~= rdsz then
        log.printf("usnetpack _decode_headex rdsz error! rdsz==>%s", rdsz);
        return;
    end

    ret.encrypt_type = cbencrypttype;
    ret.compress_type = cbcompresstype;

    pbuffer = string.sub(pbuffer, noffset, sz);
    sz = sz - rdsz;

    -- check compress
    if 0 ~= cbcompresstype then
        -- mark: wait implement
    end

    -- check encrypt
    if 0 ~= cbencrypttype then
        -- mark: wait implement
    end

    return ret, pbuffer, sz;
end

-- decode net packet head
local function _decode_head(pbuffer, sz)
    -- decode failed
    if 11 > sz then
        log.printf("netpack _decode_head sz error! sz==>%s", sz);
        return
    end

    local ret = {}

    local noffset = nil;
    local wpacketsz;
    local wprototype;
    local wprotoid;
    wpacketsz, cbrequesttype, uisessionid, wprototype, wprotoid, noffset = string.unpack(">HBI4HH", pbuffer, noffset);

    -- decode failed
    local rdsz = noffset - 1;
    if 11 ~= rdsz then
        log.printf("netpack _decode_head rdsz error! rdsz==>%s", rdsz);
        return;
    end

    ret.wpacketsize = wpacketsz;
    ret.request_type = cbrequesttype;
    ret.session_id = uisessionid;
    ret.proto_type = wprototype;
    ret.proto_id = wprotoid;

    pbuffer = string.sub(pbuffer, noffset, sz);
    sz = sz - rdsz;

    return ret, pbuffer, sz;
end

-- decode data
local function _decode_data(pbuffer, sz)
    if sz < 3 then
        log.printf("netpack _decode_data sz error! sz==>%s", sz);
        return;
    end

    local ret = {}

    local noffset = nil;
    local wdatasz;
    wdatasz, noffset = string.unpack(">H", pbuffer, noffset);
    local rdsz = noffset - 1;
    if sz ~= wdatasz+2 or 2 ~= rdsz then
        log.printf("netpack _decode_data wdatasz or rdsz error! sz==>%s wdatasz==>%s rdsz==>%s", sz, wdatasz, rdsz);
        return;
    end

    pdata = string.sub(pbuffer, noffset, sz);

    ret.size = wdatasz;
    ret.buffer = pdata;

    return ret;
end

-- decode buffer
function netpack.decode(inbuffer, sz)
    local packet = {}

    local pbuffer = inbuffer;
    local pheadex;
    local phead;
    local pdata;
    local ncuridx;
    local ntmpsz = sz;

    -- check decode headex
    if ENUM_BOOL_USEHEADEX then
        pheadex, pbuffer, ntmpsz = _decode_headex(pbuffer, sz);
        if nil == pheadex then
            return
        end
    end
    packet.headex = pheadex;

    -- decode headex
    phead, pbuffer, ntmpsz = _decode_head(pbuffer, ntmpsz);
    if nil == phead then
        return;
    end
    packet.head = phead;

    -- decode data
    pdata = _decode_data(pbuffer, ntmpsz);
    if nil == pdata then
        return;
    end
    packet.data = pdata;

    return packet;
end

return netpack;

